import { inject, getCurrentInstance } from 'vue'
import ky from 'ky'
import { isObject } from 'lodash-es' 
import router from '/@/router'

import { useCache } from '/@/cache'
import { TOKEN_KEY, REFRESH_TOKEN_KEY } from '/@/config'
import { polySymbol } from '/@/utils/helper'

const beforeRequest = [
  async (request, options) => {
    const { authorize } = options

    if (authorize) {
      const token =
        useCache().getItem(TOKEN_KEY) ||
        await refreshToken()

      if (token) {
        request.headers.set('Authorization', 'Bearer ' + token)
      } else {
        if (window.$message) {
          window.$message.error('用户未登录')
        }
  
        router.push('/login')
      }
    }
  }
]

const afterResponse = [
  async (request, options, response) => {
    const cache = useCache()
    const { authorize, slient, isText = false } = options

    async function executeRetry() {
      const token = await refreshToken()

      if (token) {
        request.headers.set('Authorization', 'Bearer ' + token)

        // retry with refreshed token
        return ky(request)
      } else {
        router.push('/login')
      }
    }

    if (response.status === 200) {
      if (isText) {
        return await response.text()
      }

      const { code, message: msg, data = {} } = await response.json()

      if (code === 0) {
        if (data.token) {
          cache.setItem(TOKEN_KEY, data.token)
        }
        if (data.refresh_token) {
          cache.setItem(REFRESH_TOKEN_KEY, data.refresh_token)
        }
  
        return data
      }

      if (!slient && window.$message) {
        window.$message.error(msg)
      }

      if (authorize && [4000, 4002].indexOf(code) > -1) {
        return executeRetry()
      }

    } else if (response.status === 401) { executeRetry()
    } else if (response.status === 500) {

      try {
        const { isJoi, message } = await response.json()

        if (isJoi) {
          if (!slient && window.$message) {
            window.$message.error(message)
          }
        }
      } catch (err) {}
    }
  }
]

let api
const apiKey = polySymbol('api')

function initApi() {
  const rawApi = ky.create({
    prefixUrl: '/api/system',
    hooks: { beforeRequest, afterResponse },
    throwHttpErrors: false
  })

  return api = {
    ...rawApi,
    get(url, params, options = {}) {
      if (isObject(params)) {
        options = {
          searchParams: params,
          ...options
        }
      }
      return rawApi.get(url, options).json()
    },
    post(url, params, options = {}) {
      if (isObject(params)) {
        options = {
          json: params,
          ...options
        }
      }
      return rawApi.post(url, options).json()
    },
    upload(url, formdata, options = {}) {
      return rawApi.post(url, { body: formdata, ...options }).json()
    },
    text(url, params, options = {}) {
      if (isObject(params)) {
        options = {
          searchParams: params,
          isText: true,
          ...options
        }
      }
      return rawApi.get(url, options).text()
    }
  }
}

export function setupApi(app) {
  app.provide(apiKey,
    app.config.globalProperties.$api = initApi()
  )
}

export function useApi() {
  if (getCurrentInstance()) {
    const $api = inject(apiKey)

    if ($api) return $api
  }

  return api || initApi()
}

export async function refreshToken() {
  const cache = useCache()
  const refreshToken = cache.getItem(REFRESH_TOKEN_KEY)

  cache.removeItem(TOKEN_KEY)

  if (refreshToken) {

    if (window.$message) {
      window.$message.info('登录中...')
    }

    try {
      const { code, data } = await ky.create({ prefixUrl: '/api' })
        .get('refresh_token', {
          searchParams: { token: refreshToken }
        })
        .json()

      if (window.$message) {
        window.$message.destroyAll()
      }

      if (code === 0) {
        const { token, refresh_token } = data

        cache.setItem(TOKEN_KEY, token)
        cache.setItem(REFRESH_TOKEN_KEY, refresh_token)

        return token
      } else {
        cache.removeItem(REFRESH_TOKEN_KEY)
      }
    } catch (err) {
      // refresh_token 401
      cache.removeItem(REFRESH_TOKEN_KEY)
      router.push('/login')
    }
  }
}

export function getHeaders() {
  const token = useCache().getItem(TOKEN_KEY)

  if (token) {
    return {
      Authorization: 'Bearer ' + token
    }
  }
}
